package org.zoomdev.zoom.dao.configs;

import org.zoomdev.zoom.common.filter.Filter;
import org.zoomdev.zoom.dao.DaoException;
import org.zoomdev.zoom.dao.SqlBuilder;
import org.zoomdev.zoom.dao.SqlConfig;
import org.zoomdev.zoom.dao.driver.SqlDriver;
import org.zoomdev.zoom.dao.entity.Entity;
import org.zoomdev.zoom.dao.entity.Field2ColumnMappingConfig;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public final class Update {

    private final ArrayList<Field2ColumnMappingConfig> updateFields = new ArrayList<>(16);
    private final ArrayList<Object> values = new ArrayList<>(16);
    private final ArrayList<String> placeHolders = new ArrayList<>(16);
    private final ArrayList<String> columns = new ArrayList<>(16);

    private final SqlDriver driver;

    public Update(SqlDriver driver) {
        this.driver = driver;
    }

    public void clear() {
        updateFields.clear();
        values.clear();
        placeHolders.clear();
        columns.clear();
    }

    public int setParameter(PreparedStatement stmt, int index) throws SQLException {
        for (int i = 0, c = values.size(); i < c; ++i) {
            Object value = values.get(i);
            Field2ColumnMappingConfig config = updateFields.get(i);
            if (value == null) {
                stmt.setNull(index++, config.getJdbcType());
            } else {
                config.setNonnullParameter(stmt, index++, value);
            }
        }
        return index;
    }


    public String buildSql(SqlConfig cfg, Where where) {
        StringBuilder sql = cfg.getSqlBuilder();
        sql.append("UPDATE ");
        driver.protectTable(sql, cfg.getTable()).append(" SET ");
        boolean first = true;

        ArrayList<String> columns = this.columns;
        for (int i = 0, c = columns.size(); i < c; ++i) {
            String column = columns.get(i);
            String placeholder = this.placeHolders.get(i);
            if (first) {
                first = false;
            } else {
                sql.append(SqlBuilder.COMMA);
            }
            driver.protectColumn(sql, column).append("=")
                    .append(placeholder);
        }


        where.append(sql);
        return sql.toString();
    }

    public void prepare(SqlConfig cfg, Entity entity, Object data, boolean ignoreNull) {
        Where where = cfg.getWhere();

        Field2ColumnMappingConfig[] toDatabases = entity.getToDatabases();
        Filter<Field2ColumnMappingConfig> filter = cfg.getFilter();
        for (Field2ColumnMappingConfig toDatabase : toDatabases) {
            if (filter != null && !filter.accept(toDatabase)) {
                continue;
            }
            String updatePlaceholder = toDatabase.updatePlaceholder;
            if (updatePlaceholder != null && !updatePlaceholder.isEmpty()) {
                columns.add(toDatabase.getColumnName());
                placeHolders.add(updatePlaceholder);
            } else {
                Object value = toDatabase.get(data);
                if (value == null && ignoreNull) {
                    continue;
                }
                columns.add(toDatabase.getColumnName());
                placeHolders.add("?");
                addValue(toDatabase, value);
            }
        }

        if (!where.hasCondition()) {
            if (entity.getPrimaryKeys() != null) {
                for (Field2ColumnMappingConfig pk : entity.getPrimaryKeys()) {
                    Object value = pk.get(data);
                    if (value == null) {
                        throw new DaoException("主键值为null:" + pk.getColumnName());
                    }
                    where.where(pk, value);
                }
            }
            if (!where.hasCondition()) {
                throw new DaoException("至少需要一个where条件");
            }
        }

        if (cfg.getTable() == null) {
            cfg.setTable(entity.getTableName());
        }
    }

    public void addValue(Field2ColumnMappingConfig field, Object value) {
        updateFields.add(field);
        values.add(value);
    }

    public List<Object> getParameterValues(Where where) {
        List<Object> whereValues = where.getValues();
        List<Object> data = new ArrayList<>(whereValues.size() + values.size());
        data.addAll(values);
        data.addAll(whereValues);
        return data;
    }
}
